import ProposalAPI from '@/api/Proposal';
import {
  Box,
  concatName,
  createTransportHooks,
  Expandable,
  FieldCols,
  FieldEffect,
  FormSection,
  Icon,
  mapField,
  NamedUpload,
  PopoverConfirm,
  SortTable,
  useCall,
  useDebounceValue,
  useDictionary,
  useEffectRef,
  useFieldName,
  useFormValue,
  useReq,
  useSimpleMemo,
} from '@/components';
import { StateProvider, useSelector, useUpdater } from '@/components/StateProvider';
import { dictToMap, forkHandler, isEqual, isEqualStrictDeep, optionsToMap } from '@/utils';
import { Button, Checkbox, Col, Form, Input, Row, Space, Spin, Tabs } from 'antd';
import groupBy from 'loadsh/groupBy';
import React, {
  cloneElement,
  createContext,
  forwardRef,
  memo,
  useContext,
  useEffect,
  useImperativeHandle,
  useMemo,
  useRef,
} from 'react';
import styles from '../index.less';
import { useDefinitionData } from './Context';

const STANDALONE = Object.freeze(['PI', 'PDF_HEAD', 'PDF_TAIL']);
const SORT = Object.freeze(['PSL_SORT', 'PDF_SORT']);

const isShowOptions = [
  { label: '产品详情页（小程序）', value: '1' },
  { label: '产品详情页（PC）', value: '3' },
  { label: 'PDF计划书', value: '2' },
];

const isShowMap = optionsToMap(isShowOptions);

const contentTypeOptions = [
  { label: '富文本', value: '1' },
  { label: '附件', value: '2' },
];

const FIELDS = [
  /** */
  [
    'options',
    null,
    'dict',
    {
      code: 'productContent',
      initialValue: STANDALONE,
      component: Checkbox.Group,
      // STANDALONE 不可取消
      getValueFromEvent: (v) => Array.from(new Set(v.concat(STANDALONE))),
      // STANDALONE 排序固定在前面
      format: (opts) =>
        opts
          .sort((a, b) => {
            const aIndex = STANDALONE.indexOf(a.code);
            const bIndex = STANDALONE.indexOf(b.code);
            if (aIndex === -1 && bIndex === -1) return 0;
            if (aIndex === -1) return 1;
            if (bIndex === -1) return -1;
            return aIndex - bIndex;
          })
          .filter((v) => !SORT.includes(v.code))
          .map((v) => ({ label: v.cnName, value: v.code })),
    },
  ],
].map(mapField);

const colProps = { xs: 24 };

const Context = createContext({});
const PureFormItem = memo(Form.Item, isEqualStrictDeep);
const PureForm = memo(Form, isEqualStrictDeep);
const PureFieldCols = memo(FieldCols, isEqualStrictDeep);

const useGetTitle = () => {
  const { form, showContentMap } = useContext(Context);
  const titles = useFormValue(form, 'titles');
  return useCall((key) => titles?.[key] ?? showContentMap?.[key] ?? key);
};

/** isShow 不重叠选中的复选框 */
function UniqCheckbox({ name, showContentType, ...props }) {
  const { form } = useContext(Context);
  const otherChecked = useFormValue(form, ['list', showContentType], (v) =>
    v?.reduce((r, e, i) => [...r, ...((i !== name && e?.isShow) || [])], []),
  );
  const isShowOptionsUniq = useSimpleMemo(
    isShowOptions.map((v) => ({ ...v, disabled: otherChecked?.includes(v.value) })),
  );
  return (
    <>
      <Checkbox.Group {...props} options={isShowOptionsUniq} />
    </>
  );
}

/** 提供新增事件/判断是否禁用 (状态下沉优化) */
const ShowContentAdd = ({ onAdd, showContentType, children }) => {
  const { form, productCode, combinationType } = useContext(Context);

  const allChecked = useFormValue(
    form,
    ['list', showContentType],
    (v) => new Set(v?.reduce((r, e) => [...r, ...e?.isShow], [])),
    (a, b) => !isEqual(Array.from(a), Array.from(b), { deep: true }),
  );

  const handleAdd = useCall(() => {
    onAdd?.({
      showContentType,
      isShow: [],
      contentType: '1',
      relationCode: productCode,
      combinationType,
      attachmentType: null,
      attachmentUrl: null,
      richTextInfo: null,
    });
  });

  return cloneElement(children, { disabled: allChecked.size >= 3, onClick: handleAdd });
};

function ToggleAndAdd({ showContentType, onAdd }) {
  return (
    <Expandable.Trigger>
      {({ expanded, toggle }) => (
        <Space size='small'>
          <ShowContentAdd
            onAdd={useCall(forkHandler(() => !expanded && toggle(), onAdd))}
            showContentType={showContentType}
          >
            <Button size='small' type='link'>
              +添加
            </Button>
          </ShowContentAdd>
          <Button type='link' onClick={toggle}>
            {expanded ? '收起' : '展开'}
          </Button>
        </Space>
      )}
    </Expandable.Trigger>
  );
}

const uploadProps = {
  action: '/UploadAttachmentController/uploadFiles',
  fileNameProp: 'richTextInfo',
  fileTypeProp: 'attachmentType',
  getFieldProps() {
    const { productCode } = useContext(Context);
    return useSimpleMemo({ data: { fileType: 'A2', relationCode: productCode } });
  },
};

const showContentItemFields = (scope, contentType) =>
  [
    ['isShow', '适用于', UniqCheckbox],
    ['contentType', '类型', 'radio-group', { options: contentTypeOptions }],
    [
      'richTextInfo',
      '富文本',
      'editor',
      {
        preserve: false,
        colProps: { hidden: contentType !== '1' },
        type: 'simple',
        itemProps: { wrapperCol: { span: 24 }, labelCol: { span: 24 } },
      },
    ],
    [
      'attachmentUrl',
      '附件',
      'named-upload',
      { preserve: false, colProps: { hidden: contentType === '1' }, ...uploadProps },
    ],
    ['richTextInfo', '附件文件名', null, { preserve: false, colProps: { hidden: contentType === '1' } }],
  ].map(mapField, { scope, merge: { preserve: false } });

const fieldProps = {
  colProps: { xs: 24 },
  itemProps: { labelCol: { xs: { span: 6 } }, wrapperCol: { xs: { span: 12 }, xl: { span: 18 } } },
};

/** ProductInfoItem 表单 */
ShowContentItem = memo(ShowContentItem, isEqualStrictDeep);

function ShowContentItem({ showContentType, field, size, remove, index }) {
  const { form } = useContext(Context);
  const contentType = useFormValue(form, ['list', showContentType, field.name, 'contentType']);

  const onRemove = useCall(remove, index);

  return (
    <Col xs={24} xl={12}>
      <div className={styles.showContentItem}>
        <Box className={styles.toolbar} flex jus='end'>
          {size > 1 && (
            <PopoverConfirm content='是否确认删除' onOk={onRemove}>
              <Button danger type='link' size='small' icon={<Icon type='close' />} />
            </PopoverConfirm>
          )}
        </Box>
        <Box px={2} py={1}>
          <PureFieldCols form={form} {...fieldProps} fields={showContentItemFields(field.name, contentType)} />
        </Box>
      </div>
    </Col>
  );
}

/** 自动添加 */
function InsertIfNeed({ showContentType, fields, add }) {
  const { productCode, combinationType } = useContext(Context);
  useEffect(() => {
    if (!fields?.length) {
      add?.({
        showContentType,
        isShow: ['1'],
        contentType: '1',
        relationCode: productCode,
        combinationType,
        attachmentType: null,
        attachmentUrl: null,
        richTextInfo: null,
      });
    }
  }, [fields]);

  return null;
}

/** 调用hook (Form.List 中无法直接使用hook) */
function Hook({ hook, value }) {
  hook(useEffectRef(value));
  return null;
}

/** 渲染单个分类 */
const ShowContentType = ({ showContentType }) => {
  const { showContentMap } = useContext(Context);
  const [useProvideAdd, useListAdd] = useMemo(() => createTransportHooks(), []);
  const [useProvideRemove, useListRemove] = useMemo(() => createTransportHooks(), []);

  const listAdd = useListAdd();
  const listRemove = useListRemove();
  const onRemove = useCall((fn, index) => {
    fn?.(index);
  }, listRemove);

  const onAdd = useCall((fn, value) => {
    fn?.(value);
  }, listAdd);

  const editableTitle = (
    <PureFormItem
      style={{ marginBottom: 0 }}
      labelCol={{ span: 0 }}
      wrapperCol={{ span: 24 }}
      name={['titles', showContentType]}
      initialValue={showContentMap[showContentType]}
    >
      <Input />
    </PureFormItem>
  );

  return (
    <Expandable defaultExpanded>
      <FormSection title={editableTitle} right={<ToggleAndAdd showContentType={showContentType} onAdd={onAdd} />}>
        <Expandable.Content>
          <Form.List name={['list', showContentType]}>
            {(fields, { add, remove }) => (
              <>
                <Hook hook={useProvideAdd} value={add} />
                <Hook hook={useProvideRemove} value={remove} />
                <InsertIfNeed showContentType={showContentType} fields={fields} add={add} />
                <Row gutter={16}>
                  {fields.map((field, index) => (
                    <ShowContentItem
                      key={field.key}
                      showContentType={showContentType}
                      field={field}
                      size={fields.length}
                      remove={onRemove}
                      index={index}
                    />
                  ))}
                </Row>
              </>
            )}
          </Form.List>
        </Expandable.Content>
      </FormSection>
    </Expandable>
  );
};

/** 渲染选中的分类 (状态下沉优化) */
function ShowContents() {
  const { form } = useContext(Context);
  const options = useFormValue(form, 'options', (opts) => opts?.filter((v) => !STANDALONE.includes(v)) ?? []);

  return options.map((showContentType) => <ShowContentType key={showContentType} showContentType={showContentType} />);
}

SingleAttachment = memo(SingleAttachment, isEqualStrictDeep);

function SingleAttachment({ showContentType, isShow, ...rest }) {
  const { productCode, combinationType } = useContext(Context);
  const name = concatName(useFieldName(), '../');
  const data = useMemo(() => ({ fileType: 'A2', relationCode: productCode }), []);

  return (
    <>
      <NamedUpload data={data} fileNameProp='richTextInfo' fileTypeProp='attachmentType' {...rest} />
      <FieldEffect name={concatName(name, 'showContentType')} value={showContentType} />
      <FieldEffect name={concatName(name, 'isShow')} value={[isShow]} />
      <FieldEffect name={concatName(name, 'relationCode')} value={productCode} />
      <FieldEffect name={concatName(name, 'combinationType')} value={combinationType} />
      <FieldEffect name={concatName(name, 'contentType')} value='1' />
    </>
  );
}

function StandalonePI() {
  const { form, showContentMap, productCode } = useContext(Context);
  const indexOf1 = useFormValue(form, ['list', 'PI'], (l) => l?.findIndex((v) => v.isShow?.includes('1')) ?? -1);
  const indexOf2 = useFormValue(form, ['list', 'PI'], (l) => l?.findIndex((v) => v.isShow?.includes('2')) ?? -1);
  const indexOf3 = useFormValue(form, ['list', 'PI'], (l) => l?.findIndex((v) => v.isShow?.includes('3')) ?? -1);
  let curr = Math.max(indexOf1, indexOf2, indexOf3);
  const next = () => (curr += 1);

  const indexes = {
    1: indexOf1 > -1 ? indexOf1 : next(),
    2: indexOf2 > -1 ? indexOf2 : next(),
    3: indexOf3 > -1 ? indexOf3 : next(),
  };

  const fields = useSimpleMemo(
    [
      [[indexes[1], 'attachmentUrl'], isShowMap[1], SingleAttachment, { isShow: '1' }],
      [[indexes[2], 'attachmentUrl'], isShowMap[2], SingleAttachment, { isShow: '2' }],
      [[indexes[3], 'attachmentUrl'], isShowMap[3], SingleAttachment, { isShow: '3' }],
    ].map(mapField, { scope: ['list', 'PI'], merge: { showContentType: 'PI' } }),
  );

  return (
    <>
      <FieldEffect name={['list', 'PI']} initialValue={[]} />
      <Expandable defaultExpanded>
        <FormSection
          title={showContentMap.PI}
          right={
            <Expandable.Trigger>
              {({ expanded, toggle }) => (
                <Button type='link' onClick={toggle}>
                  {expanded ? '收起' : '展开'}
                </Button>
              )}
            </Expandable.Trigger>
          }
        >
          {!!productCode && (
            <Expandable.Content>
              <PureFieldCols colProps={{ xs: 24 }} form={form} labelSpan={8} wrapperSpan={12} fields={fields} />
            </Expandable.Content>
          )}
        </FormSection>
      </Expandable>
    </>
  );
}

function StandalonePDF() {
  const { form, showContentMap, productCode } = useContext(Context);

  const labelHead = showContentMap?.PDF_HEAD || 'PDF封面';
  const labelTail = showContentMap?.PDF_TAIL || '封底';

  const fields = useSimpleMemo(
    [
      [
        ['list', 'PDF_HEAD', 0, 'attachmentUrl'],
        labelHead,
        SingleAttachment,
        { isShow: '2', showContentType: 'PDF_HEAD' },
      ],
      [
        ['list', 'PDF_TAIL', 0, 'attachmentUrl'],
        labelTail,
        SingleAttachment,
        { isShow: '2', showContentType: 'PDF_TAIL' },
      ],
    ].map(mapField),
  );

  return (
    <>
      <FieldEffect name={['list', 'PDF_HEAD']} initialValue={[]} />
      <FieldEffect name={['list', 'PDF_TAIL']} initialValue={[]} />
      <Expandable defaultExpanded>
        <FormSection
          title={`${labelHead}&${labelTail}`}
          right={
            <Expandable.Trigger>
              {({ expanded, toggle }) => (
                <Button type='link' onClick={toggle}>
                  {expanded ? '收起' : '展开'}
                </Button>
              )}
            </Expandable.Trigger>
          }
        >
          {!!productCode && (
            <Expandable.Content>
              <PureFieldCols form={form} fields={fields} colProps={{ xs: 24 }} labelSpan={8} wrapperSpan={12} />
            </Expandable.Content>
          )}
        </FormSection>
      </Expandable>
    </>
  );
}

const unGroupList = (group, map = (v) => v, filter = () => true) =>
  Object.keys(group ?? {}).reduce((r, k) => [...r, ...group?.[k]?.filter(filter)?.map(map)], []);

const getTypes = (v, type) =>
  Array.from(
    new Set(
      unGroupList(
        v,
        (i) => i.showContentType,
        (i) => !STANDALONE.includes(i.showContentType) && i.isShow?.includes(type),
      ),
    ),
  );

const mergeList = (list, newList) => {
  list = list ?? [];
  newList = newList ?? [];
  const rmList = list.filter((v) => !newList.includes(v));
  const addList = newList.filter((v) => !list.includes(v));
  return list.filter((v) => !rmList.includes(v)).concat(addList);
};

const sortList = (list, dragIndex, hoverIndex) => {
  const data = list.slice(0);
  const [item] = data.splice(dragIndex, 1);
  data.splice(hoverIndex, 0, item);
  return data;
};

const useSortTableConfig = (type) => {
  const key = { 1: 'psl', 2: 'pdf' }[type];
  const { form } = useContext(Context);
  const update = useUpdater();
  const getTitle = useGetTitle();

  const list = useSelector((state) => state[key]?.list ?? []);
  const selectedRowKeys = useSelector((state) => state[key]?.selected ?? []);

  const dataSource = useSimpleMemo((list ?? []).map((v) => ({ value: v, label: getTitle(v) })));

  const nextList = useDebounceValue(useFormValue(form, 'list', (v) => getTypes(v, type)));
  useEffect(() => {
    if (!nextList) return;
    let selected = selectedRowKeys;
    let newList = list.concat(nextList.filter((v) => !list.includes(v)));
    newList = newList.filter((v) => nextList.includes(v));
    newList.forEach((v) => {
      if (!list.includes(v)) selected = selected.concat([v]);
    });
    update({ [key]: { selected, list: newList } });
  }, [nextList]);

  const onChange = useCall((selected) => {
    update({ [key]: { selected } });
  });

  const onSort = useCall((dragIndex, hoverIndex) => {
    update({ [key]: { list: sortList(list, dragIndex, hoverIndex) } });
  });

  return useSimpleMemo({
    dataSource,
    rowSelection: { selectedRowKeys, onChange },
    onSort,
  });
};

const SortConfigProposal = () => (
  <SortTable rowKey='value' columns={[{ dataIndex: 'label' }]} {...useSortTableConfig('1')} />
);

const SortConfigPDF = () => (
  <SortTable rowKey='value' columns={[{ dataIndex: 'label' }]} {...useSortTableConfig('2')} />
);

const form2Data = (formData, sortData, context) => {
  const { showContentMap, productCode, combinationType } = context;
  console.log('formData', formData);
  const { list, titles } = formData;
  let {
    psl: { item: pslSortItem, list: pslSortKeys, selected: pslSortSelected },
    pdf: { item: pdfSortItem, list: pdfSortKeys, selected: pdfSortSelected },
  } = sortData;

  pslSortKeys = pslSortKeys ?? [];
  pdfSortKeys = pdfSortKeys ?? [];
  pslSortSelected = pslSortSelected ?? [];
  pdfSortSelected = pdfSortSelected ?? [];

  const pslSortItemNew = {
    showContentType: 'PSL_SORT',
    showContentTitle: showContentMap.PSL_SORT,
    relationCode: productCode,
    contentType: '1',
    combinationType,
    ...pslSortItem,
    isShow: '0',
    richTextInfo: pslSortKeys.filter((v) => pslSortSelected.includes(v)).join(','),
  };
  const pdfSortItemNew = {
    ...pdfSortItem,
    showContentType: 'PDF_SORT',
    showContentTitle: showContentMap.PDF_SORT,
    relationCode: productCode,
    contentType: '1',
    combinationType,
    isShow: '0',
    richTextInfo: pdfSortKeys.filter((v) => pdfSortSelected.includes(v)).join(','),
  };

  return Object.keys(list).reduce(
    (ret, key) => [
      ...ret,
      ...list[key]?.filter(Boolean).map((v) => ({
        ...v,
        attachmentUrl: v.attachmentUrl ?? '',
        richTextInfo: v.richTextInfo ?? '',
        showContentTitle: titles?.[key] ?? showContentMap?.[key] ?? key,
        isShow: (v.isShow ?? []).join(','),
      })),
    ],
    [pslSortItemNew, pdfSortItemNew],
  );
};

const data2Form = (resultData) => {
  const { PDF_SORT, PSL_SORT, ...list } = groupBy(
    resultData.map((v) => ({ ...v, isShow: (v.isShow ?? '').split(',') })),
    'showContentType',
  );

  const pslSortItem = PSL_SORT?.[0] ?? null;
  const pdfSortItem = PDF_SORT?.[0] ?? null;

  const pslSort = pslSortItem?.richTextInfo?.split(',').filter(Boolean) ?? [];
  const pdfSort = pdfSortItem?.richTextInfo?.split(',').filter(Boolean) ?? [];

  const pslSortKeys = Array.from(new Set(pslSort.concat(getTypes(list, '1'))));
  const pdfSortKeys = Array.from(new Set(pdfSort.concat(getTypes(list, '2'))));

  const options = Array.from(new Set(Object.keys(list).concat(STANDALONE)));

  const sortData = {
    psl: { item: pslSortItem, list: pslSortKeys, selected: pslSort },
    pdf: { item: pdfSortItem, list: pdfSortKeys, selected: pdfSort },
  };

  const titles = Object.keys(list ?? {}).reduce(
    (ret, typeKey) => ({
      ...ret,
      [typeKey]: list[typeKey]?.[0]?.showContentTitle,
    }),
    {},
  );

  const formData = { list, titles, options };
  return [formData, sortData];
};

const INIT_SORT = {
  psl: {
    item: null,
    list: [],
    selected: [],
  },
  pdf: {
    item: null,
    list: [],
    selected: [],
  },
};

function ShowContentConfig(props, ref) {
  const definitionData = useDefinitionData();
  const stateRef = useRef();
  const xhr = useReq(ProposalAPI.getProductInfo);
  const showContentMap = useDictionary('productContent', false, dictToMap);
  const { productCode, combinationType } = definitionData?.productDefineList?.[0] ?? {};
  const refresh = useCall(() => xhr.start({ Code: productCode, combinationType }));
  const [form] = Form.useForm();

  const context = useSimpleMemo({ productCode, combinationType, showContentMap, form });

  useEffect(() => {
    if (productCode) refresh();
  }, [productCode]);

  useEffect(() => {
    if (xhr.result?.data) {
      const [formData, sortData] = data2Form(xhr.result.data);
      console.log({ formData, sortData });
      stateRef.current?.setState?.(sortData);
      form.setFieldsValue(formData);
    }
  }, [xhr.result]);

  const onSave = useCall(() => {
    const formData = form.getFieldsValue();
    const sortData = stateRef.current?.getState?.();
    return ProposalAPI.insertProductInfo(form2Data(formData, sortData, context));
  });

  useImperativeHandle(ref, () => ({ onSave }), []);

  return (
    <Context.Provider value={context}>
      <StateProvider ref={stateRef} initialValue={INIT_SORT}>
        <Spin spinning={xhr.status === 'loading'}>
          <PureForm form={form} labelCol={{ span: 6 }} wrapperCol={{ xs: { span: 12 }, xl: { span: 18 } }}>
            <FormSection title='配置'>
              <PureFieldCols form={form} colProps={colProps} wrapperSpan={24} labelSpan={0} fields={FIELDS} />
            </FormSection>

            <Row gutter={16}>
              <Col xs={24} xl={12}>
                <StandalonePI />
              </Col>
              <Col xs={24} xl={12}>
                <StandalonePDF />
              </Col>
            </Row>

            <ShowContents />

            <FormSection title='排序配置'>
              <Tabs defaultActiveKey='pslSort'>
                <Tabs.TabPane tab='计划书预览页' key='pslSort'>
                  <p>拖动下列名称可进行排序，排序的结果决定了展业平台对应页面内容的展示顺序</p>

                  <SortConfigProposal />
                </Tabs.TabPane>
                <Tabs.TabPane tab='PDF计划书' key='pdfSort'>
                  <p>拖动下列名称可进行排序，排序的结果决定了展业平台对应页面内容的展示顺序</p>
                  <SortConfigPDF />
                </Tabs.TabPane>
              </Tabs>
            </FormSection>
          </PureForm>
        </Spin>
      </StateProvider>
    </Context.Provider>
  );
}

ShowContentConfig = forwardRef(ShowContentConfig);

export default ShowContentConfig;
